/**
 * Copyright 2007 Igor Chaves Cananéa
 *
 * This file is part of PBSFW
 *
 * PBSFW is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * PBSFW is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef _PROCESS_H_
#define _PROCESS_H_

#include <pcl.h>
#include <iostream>
#include <vector>
#include <list>
#include <cstring>
#include <cstdarg>
#include "defines.h"
#include "store.h"
#include "simevent.h"
#include "task.h"
#define STACK_SIZE (32 * 1024)
using namespace std;

namespace pbsfw {
    class RefCount {
        private:
            int32_t theRefCount;

        public:
            RefCount() : theRefCount(0) {}

            void incRef() {
                ++this->theRefCount;
            }

            void decRef() {
                --this->theRefCount;
            }

            int32_t refCount() const {
                return this->theRefCount;
            }
    };

    class Resource;
    class Task;
    typedef vector<SimEvent*> SimEventVector;
    class Process : public RefCount {
        private:
            static uint32_t nextId;

            static void entry(void *args);
            static uint32_t getNextId() {
                return ++Process::nextId;
            }

            uint32_t id;
            coroutine_t coRoutine;
            bool active;
            bool dead;
            string name;
            Task *nextTask;
            SimEventVector &eventsFired;
            SimEventVector &eventsWaiting;
            unsigned char stack[STACK_SIZE];

            void removeFromEventWaitQueue() {
                uint size = this->eventsWaiting.size();
                for (uint i = 0; i < size; ++i) {
                    this->eventsWaiting[i]->removeProcess(*this);
                }
            }

            void do_run() {
                this->coRoutine = co_create(Process::entry, this, this->stack, STACK_SIZE);
                this->incRef();
            }

            bool do_waitfor(uint qntEvents, va_list ap);

        protected:
            bool canDelete;

        public:
            Process(string name = "a_Process");

            virtual ~Process() {
                delete &this->eventsFired;
                delete &this->eventsWaiting;
                this->cleanup();
            }

            void setNextTask(Task &t) {
                this->nextTask = &t;
            }

            void invalidateNextTask() {
                if (this->nextTask != 0) {
                    this->nextTask->cancelExecution();
                    this->nextTask = 0;
                }
            }

            virtual void cleanup() {
                if (this->canDelete) {
                    if (!this->dead) {
                        this->decRef();
                        co_delete(this->coRoutine);
                    }
                    this->canDelete = false;
                    this->dead = true;
                    this->active = false;
                }
            }
            virtual void run() = 0;

            const char *getName() const {
                return this->name.c_str();
            }

            void setActive(bool active) {
                this->active = active;
            }

            bool isActive() const {
                return this->active;
            }

            void keepGoing() {
                if (this->dead) {
                    this->dead = false;
                    this->canDelete = true;
                    this->do_run();
                }
                co_call(this->coRoutine);
            }

            bool operator==(const Process &p) const {
                return this->coRoutine == p.coRoutine;
            }

            bool operator<(const Process &p) const {
                return this->coRoutine < p.coRoutine;
            }

            uint32_t getId() const {
                return this->id;
            }

            void hold(double howMuch = 0.0);
            void passivate();

            void waitfor(SimEvent *event);
            void waitfor(uint qntEvents, ...);

            void addFiredEvent(SimEvent &event) {
                this->eventsFired.push_back(&event);
            }

            SimEventVector getFiredEvents();

            void request(Resource &r);
            void release(Resource &r);
            virtual void simulationEnd() {}


            template<typename OBJECT>
            void put(Store<OBJECT> &s, OBJECT obj) {
                if (s.put(*this, obj) == false) {
                    this->passivate();
                    s.put(*this, obj);
                }
            }

            template<typename OBJECT>
            void putMany(Store<OBJECT> &s, list<OBJECT> &l) {
                if (!l.empty()) {
                    uint amount = l.size();
                    if (s.put(*this, amount, l) == false) {
                        this->passivate();
                        s.put(*this, amount, l);
                    }
                }
            }

            template<typename OBJECT>
            OBJECT get(Store<OBJECT> &s) {
                OBJECT obj;

                if (s.get(*this, obj) == false) {
                    this->passivate();
                    s.get(*this, obj);
                }

                return obj;
            }

            template<typename OBJECT>
            list<OBJECT> get(Store<OBJECT> &s, uint amount) {
                list<OBJECT> l;

                if (s.get(*this, amount, l) == false) {
                    this->passivate();
                    s.get(*this, amount, l);
                }
                return l;
            }
    };

};

#endif
